﻿#region copyright
//                Copyright Andrew Rafas 2012.
// Distributed under the Eclipse Public License, Version 1.0.
//           (See accompanying file LICENSE.txt or 
//     copy at http://www.eclipse.org/legal/epl-v10.html)
#endregion
using System;
using System.Collections.Generic;

namespace CSFP.Memoize
{
    public class Source<T> : IValue<T>
    {
        protected bool _isValid;
        protected T _value;
        protected DependentList _dependentList;

        public Source()
        {
            _isValid = false;
            _value = default(T);
            _dependentList = EmptyDependentList.Instance;
        }

        bool ISource.IsReady
        {
            get { return _isValid; }
        }

        void ISource.AddDependent(IDependent dependency, int key)
        {
            _dependentList = _dependentList.Add(new DepKey(dependency, key));
        }

        public bool IsValid { get { return _isValid; } }

        public T Value
        {
            get
            {
                if (!_isValid)
                    throw new Exception("SUX");
                return _value;
            }
            set
            {
#if DEBUG
                Helper.Log("Set: {0} := {1}", this, value);
#endif
                if (!_isValid) {
                    _value = value;
                    _isValid = true;
                    _dependentList.NotifyAll(DependentList.BecomeReady);
                } else {
                    // we always create a value changed event, since we cannot compare to the old value
                    _value = value;
                    _dependentList.NotifyAll(DependentList.ValueChanged);
                }
            }
        }

        public void Invalidate()
        {
#if DEBUG
            Helper.Log("Invalidate: {0}", this);
#endif
            if (_isValid) {
                _value = default(T); // releases if a reference
                _isValid = false;
                _dependentList.NotifyAll(DependentList.BecomeNotReady);
            }
        }

        public override string ToString()
        {
            return _isValid ? _value.ToString() : "-";
        }
    }

    public sealed class SourceValue<T> : Source<T>
        where T : IEquatable<T>
    {
        public new T Value
        {
            get { return base.Value; }
            set
            {
                if (!_isValid || !_value.Equals(value))
                    base.Value = value;
                // otherwise there is no change at all, do nothing
            }
        }
    }

    public sealed class Constant<T> : IValue<T>
    {
        readonly T _value;

        public Constant(T value)
        {
            _value = value;
        }

        public T Value { get { return _value; } }
        bool ISource.IsReady { get { return true; } }
        void ISource.AddDependent(IDependent dependency, int key) { } // since we will not notify anybody, do not even keep track of it
    }
}
